package main

import (
	"crypto/tls"
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/gorilla/websocket"

	// 引入 MySQL 驱动，使其自动注册到 database/sql
	_ "github.com/go-sql-driver/mysql"
)

// 定义一个结构体来匹配 JSON 结构
type Message struct {
	ID         int64  `json:"id"`         // 映射到 "id"
	SourceName string `json:"sourceName"` // 映射到 "sourceName"
	Timestamp  string `json:"timestamp"`  // timestamp
	Title      string `json:"title"`
	Content    string `json:"content"`
	MsgType    int    `json:"msgType"` // 定义消息类型，1为 服务端客户端系统消息，非通知消息
	ConnID     string `json:"connID"`
}

// 模拟的配置和密钥
const (
	HTTPPort  = "8443" // HTTPS 端口
	WSPort    = "8080" // WebSocket 端口 (可选，如果使用同一个端口，则需要更复杂的路由)
	APIKey    = "your_static_api_key"
	APISecret = "your_static_api_secret"
	CertFile  = "certs/server.crt" // 替换成你的证书路径
	KeyFile   = "certs/server.key" // 替换成你的密钥路径

	// ------------------- 数据库配置 -------------------
	DB_USER = "dingdong"
	DB_PASS = "m8*H!x9P@q0Z7$D2" // 替换为你的数据库密码
	DB_HOST = "192.168.1.13:14308"
	DB_NAME = "dingdong" // 替换为你的数据库名
)

// 全局 SQL DB 实例
var DB *sql.DB

func connectDB() {
	// 1. 初始化数据库连接
	log.Println("Initializing MySQL connection...")
	dbSource := fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true", DB_USER, DB_PASS, DB_HOST, DB_NAME)

	var err error
	// parseTime=true 允许 MySQL 驱动自动将 DATETIME 字段解析为 Go 的 time.Time 类型
	DB, err = sql.Open("mysql", dbSource)
	if err != nil {
		log.Fatalf("Error opening database: %v", err)
	}

	// 检查连接是否成功
	if err = DB.Ping(); err != nil {
		log.Fatalf("Failed to connect to MySQL: %v", err)
	}
	log.Println("MySQL connection successful!")
}

// saveToMySQL 写入 MySQL 数据库的函数
func saveToMySQL(msg Message) int64 {
	// 1. 准备 SQL 语句 (使用 ? 占位符防止 SQL 注入)
	// INSERT INTO `dingdong`.`messages` (`id`, `content`, `title`, `sent_time`, `msg_type`, `source_name`, `created_at`) VALUES (1, 'mn', 'mn', '2025-10-27 21:41:42', 23, 'api', '2025-10-27 21:41:54');

	sqlStatement := `INSERT INTO messages (content, title, sent_time, msg_type, source_name) VALUES (?, ?, ?, ?,?)`

	// 3. 执行插入
	result, err := DB.Exec(
		sqlStatement,
		msg.Content,
		msg.Title,
		msg.Timestamp,
		msg.MsgType,
		msg.SourceName,
	)

	if err != nil {
		// 写入失败，打印错误日志，但通常不应中断HTTP/WS请求
		log.Printf("[DB WRITE ERROR] Failed to insert message: %v", err)
	} else {
		log.Printf("[DB SUCCESS] Message saved to MySQL with ID:")
	}

	id, err := result.LastInsertId()
	if err != nil {
		log.Fatalf("Failed to retrieve LastInsertId: %v", err)
	}
	return id
}

// 全局WebSocket连接管理器 (简化处理，实际应用中应使用更健壮的结构)
var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	// 允许跨域（仅用于测试，生产环境应根据需要限制）
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// 存储活动连接的映射 (Key: ConnectionID, Value: *websocket.Conn)
var activeConnections = make(map[string]*websocket.Conn)
var connCounter = 0 //用于生成简单的连接ID

var timeFormat = "2006-01-02 15:04:05"

// 启动一个简单的HTTP服务，用于接收消息
func startHTTPServer() {
	// 仅为演示，因为 main 中我们使用了 /api/send 路径，所以这里不需要额外启动一个服务。
}

// ------------------- WebSocket Handler -------------------

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
	// 1. 验证 API Key/Secret (在升级前进行，或者通过WebSocket连接建立后立即要求客户端发送凭证)
	// 为了简单，这里假设我们在 `/ws` 路径上先进行一个简单的头部/查询参数验证
	key := r.URL.Query().Get("apiKey")
	secret := r.URL.Query().Get("secret")

	if key != APIKey || secret != APISecret {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	// 2. 升级连接
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("WebSocket upgrade error:", err)
		return
	}
	defer conn.Close()

	// 3. 存储连接并开始读写循环
	connID := fmt.Sprintf("conn_%d", connCounter)
	connCounter++
	activeConnections[connID] = conn
	log.Printf("New WebSocket connection established: %s", connID)

	// 4. 启动心跳/读取循环
	go wsReadLoop(conn, connID)

	// 发送连接成功的消息
	msgStr, _ := StructToStringJSON(getConnectMsg(connID))
	conn.WriteMessage(websocket.TextMessage, []byte(msgStr))

	// 保持连接开启，直到客户端关闭
	// 实际的写操作应该在接收到HTTP消息后被触发，但需要一个机制保持goroutine存活，直到连接关闭
	// 这里我们让 wsReadLoop 来处理连接的生命周期。
	// 如果不启动wsReadLoop，conn.Close() 会立即执行，连接会断开。
	// 为了让 wsReadLoop 负责清理，我们在这里可以设置一个等待，但这并不是最佳实践。
	// 最佳实践是：wsReadLoop 应该**只读**并处理断开，而**写**操作应该由其他 goroutine 触发。

	// 为了确保 goroutine 不退出，在 wsReadLoop 内部处理连接关闭。
	// 如果连接主动关闭，wsReadLoop 会退出，然后 defer conn.Close() 执行。

	// 此时我们只启动了读循环，写操作将由 handleSendAPI 触发。
	// 因为我们没有一个阻塞的写循环，所以需要确保 conn 不会被过早回收。
	// gorilla/websocket 的设计是，只要连接存在，goroutine 就会等待读事件。
	// 我们可以设置一个空循环或保持这个 goroutine 存活，直到连接关闭。

	// 简化的等待（依赖 wsReadLoop 退出时 conn.Close() 被调用）
	select {} // 阻塞当前 goroutine，直到它被触发退出
}

func wsReadLoop(conn *websocket.Conn, connID string) {
	defer func() {
		delete(activeConnections, connID)
		log.Printf("Connection closed and cleaned up: %s. Active connections: %d", connID, len(activeConnections))
	}()

	for {
		// 接收消息 (用于心跳检测)
		messageType, message, err := conn.ReadMessage()
		if err != nil {
			if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
				log.Printf("Client closed connection normally: %s", connID)
			} else {
				log.Printf("Read error on %s: %v", connID, err)
			}
			break
		}

		// 处理心跳包 (Ping/Pong) 或客户端发送的数据
		if messageType == websocket.PingMessage {
			conn.WriteMessage(websocket.PongMessage, []byte{})
			log.Printf("Received Ping from %s, sent Pong.", connID)
			continue
		}

		log.Printf("Received message from %s: %s", connID, string(message))
		// 收到客户端消息，这里可以做进一步处理或忽略
	}
}

// ------------------- HTTP Handler (接收信息) -------------------

func handleSendAPI(w http.ResponseWriter, r *http.Request) {
	// 1. 身份验证 (可以基于 Header, Body 或 Query Param)
	apiKey := r.Header.Get("X-Api-Key")
	apiSecret := r.Header.Get("X-Api-Secret")

	if apiKey != APIKey || apiSecret != APISecret {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// 2. 解析接收到的信息 (这里简化为读取请求体)
	// 实际应用中，您需要解析 JSON 或其他格式
	body := make([]byte, r.ContentLength)
	r.Body.Read(body)
	messageReceived := string(body)
	// 3. 转发给所有连接的 WebSocket 客户端
	log.Printf("Receive message from api/send: %s", messageReceived)

	var msgStruct Message
	err := JSONToStruct(body, &msgStruct)

	if err != nil {
		log.Fatalf("解析 JSON 失败: %v", err)
	}

	msgId := saveToMySQL(msgStruct)

	msgStruct.ID = msgId
	messageToSend, err := StructToStringJSON(msgStruct)
	if err != nil {
		log.Fatalf("转json失败: %v", err)
	}
	log.Printf("Forwarding message to all WebSocket clients: %s", messageToSend)
	// **注意:** 生产环境应考虑**目标明确性** (比如指定一个 ClientID) 和**错误处理** (跳过已断开的连接)
	for connID, conn := range activeConnections {
		if err := conn.WriteMessage(websocket.TextMessage, []byte(string(messageToSend))); err != nil {
			log.Printf("Error sending to %s: %v. Removing connection.", connID, err)
			// 立即清理断开的连接
			conn.Close()
			delete(activeConnections, connID)
		}
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Message forwarded successfully."))
}

// ------------------- API Handler (获取历史消息，带分页) -------------------

// handleGetHistory 从数据库中获取指定页码和大小的历史消息
func handleGetHistory(w http.ResponseWriter, r *http.Request) {
	// 1. 身份验证
	apiKey := r.Header.Get("X-Api-Key")
	apiSecret := r.Header.Get("X-Api-Secret")

	if apiKey != APIKey || apiSecret != APISecret {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// 2. 解析分页参数
	page := 1   // 默认页码
	limit := 50 // 默认每页大小，建议设置一个最大值限制，例如 100
	maxLimit := 100

	if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
		if l, err := fmt.Sscanf(limitStr, "%d", &limit); err == nil && l == 1 && limit > 0 && limit <= maxLimit {
			// 验证通过
		}
	}
	if pageStr := r.URL.Query().Get("page"); pageStr != "" {
		if p, err := fmt.Sscanf(pageStr, "%d", &page); err == nil && p == 1 && page > 0 {
			// 验证通过
		}
	}

	// 计算 OFFSET (偏移量)
	// 假设 Page 1 的偏移量是 0
	offset := (page - 1) * limit

	// 3. 查询总记录数 (用于计算总页数)
	var totalRecords int
	err := DB.QueryRow("SELECT COUNT(*) FROM messages").Scan(&totalRecords)
	if err != nil {
		log.Printf("Error counting total records: %v", err)
		http.Error(w, "Could not count total records", http.StatusInternalServerError)
		return
	}

	// 计算总页数
	totalPages := (totalRecords + limit - 1) / limit // 向上取整的公式

	// 4. SQL 查询：按 ID 降序，使用 LIMIT 和 OFFSET
	// ORDER BY id DESC 确保最新的记录在第一页
	query := `SELECT id, content, title, sent_time, msg_type, source_name FROM messages ORDER BY id DESC LIMIT ? OFFSET ?`

	rows, err := DB.Query(query, limit, offset)
	if err != nil {
		log.Printf("Error querying history: %v", err)
		http.Error(w, "Database query failed", http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	var history []Message

	// 5. 遍历结果集
	for rows.Next() {
		var msg Message
		var dbTime time.Time

		err := rows.Scan(&msg.ID, &msg.Content, &msg.Title, &dbTime, &msg.MsgType, &msg.SourceName)
		if err != nil {
			log.Printf("Error scanning row: %v", err)
			continue
		}

		msg.Timestamp = dbTime.Format(timeFormat)
		history = append(history, msg)
	}

	if err = rows.Err(); err != nil {
		log.Printf("Error during rows iteration: %v", err)
		http.Error(w, "Error processing query results", http.StatusInternalServerError)
		return
	}

	// 6. 构造包含分页信息的响应结构体
	response := struct {
		Page         int       `json:"page"`
		Limit        int       `json:"limit"`
		TotalRecords int       `json:"totalRecords"`
		TotalPages   int       `json:"totalPages"`
		Data         []Message `json:"data"`
	}{
		Page:         page,
		Limit:        limit,
		TotalRecords: totalRecords,
		TotalPages:   totalPages,
		Data:         history,
	}

	// 7. 返回 JSON 响应
	w.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(response); err != nil {
		log.Printf("Error writing JSON response: %v", err)
	}
}

// ------------------- 心跳检测 (Ping/Pong) -------------------

func connectionPinger() {
	ticker := time.NewTicker(10 * time.Second) // 每10秒检查一次
	defer ticker.Stop()

	for range ticker.C {
		// 遍历所有连接并发送 Ping
		for connID, conn := range activeConnections {
			if err := conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(3*time.Second)); err != nil {
				log.Printf("Failed to send Ping to %s: %v. Closing connection.", connID, err)
				conn.Close()
				delete(activeConnections, connID)
			}
		}
	}
}

// StructToStringJSON 将结构体实例转换为格式化的 JSON 字符串
func StructToStringJSON(data interface{}) (string, error) {
	// 1. 使用 json.Marshal 编码结构体为字节数组 []byte
	jsonData, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return "", fmt.Errorf("结构体编码为 JSON 失败: %w", err)
	}

	// 2. 将字节数组转换为字符串并返回
	return string(jsonData), nil
}

// JSONToStruct 将 JSON 格式的字节数组转换为指定的结构体类型
// data: 待解析的 JSON 数据的字节数组 ([]byte)
// target: 用于接收解析结果的结构体指针 (&MyStruct{})
func JSONToStruct(data []byte, target interface{}) error {
	// json.Unmarshal 会将 JSON 数据解析并填充到 target 指针指向的变量中
	err := json.Unmarshal(data, target)
	if err != nil {
		return fmt.Errorf("JSON 解析失败: %w", err)
	}
	return nil
}

// 返回client刚连接时，响应客户端的消息
func getConnectMsg(connID string) Message {
	return Message{
		ID:         1,
		SourceName: "server",
		Timestamp:  time.Now().Format(timeFormat),
		Title:      "客户端连接服务器成功",
		Content:    "客户端连接服务器成功",
		MsgType:    1,
		ConnID:     connID,
	}
}

func main() {

	connectDB()

	// 1. 启动HTTPS HTTP 服务
	go startHTTPServer()

	// 2. 启动WebSocket HTTP 服务（为了简单，我们使用另一个端口或路径，但这里为展示分离，我们假设它们在不同端口或需要单独升级）
	// **注意:** 生产中，通常会将WebSocket升级放在同一个HTTPS服务器上，通过路径判断。

	// 3. 启动HTTP/WebSocket 混合服务（推荐的方式）
	http.HandleFunc("/ws", handleWebSocket)
	http.HandleFunc("/api/send", handleSendAPI)
	// 注册新的历史查询 API
	http.HandleFunc("/api/history", handleGetHistory)

	// 启动心跳检查（在 handler.go 中实现更具体的逻辑）
	go connectionPinger()

	fmt.Printf("Starting HTTPS server on :%s\n", HTTPPort)
	fmt.Printf("Starting WebSocket server on :%s (upgraded from HTTP)\n", HTTPPort)
	fmt.Printf("API Key: %s, Secret: %s\n", APIKey, APISecret)

	// 使用HTTPS启动服务 (需要加载TLS配置)
	cert, err := tls.LoadX509KeyPair(CertFile, KeyFile)
	if err != nil {
		log.Fatalf("Error loading certificates: %v\n(请确保 'certs/' 目录下有 'server.crt' 和 'server.key' 且服务已启动在 '8080' 端口，或修改端口/使用HTTP)", err)
		// 如果您想在本地快速测试，可以暂时注释掉HTTPS部分，使用 http.ListenAndServe
		// log.Fatal(http.ListenAndServe(":"+HTTPPort, nil))
	}

	server := &http.Server{
		Addr:      ":" + HTTPPort,
		TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}},
		// 可以在这里设置更详细的TLS配置
	}

	if err := server.ListenAndServeTLS("", ""); err != nil {
		log.Fatalf("HTTPS server failed: %v", err)
	}
}
